﻿#include "FITKGraphObjectSpringPoint2Point.h"

// VTK
#include <vtkPoints.h>
#include <vtkUnstructuredGrid.h>
#include <vtkCellData.h>
#include <vtkProperty.h>
#include <vtkIntArray.h>
#include <vtkSelectionNode.h>
#include <vtkSelection.h>
#include <vtkExtractSelection.h>
#include <vtkIdTypeArray.h>
#include <vtkIdList.h>
#include <vtkSphereSource.h>
#include <vtkDistanceToCamera.h>
#include <vtkGlyph3D.h>
#include <vtkProperty2D.h>
#include <vtkPointData.h>
#include <vtkStringArray.h>
#include <vtkTextProperty.h>
#include <vtkPointSetToLabelHierarchy.h>
#include <vtkLabelPlacementMapper.h>
#include <vtkFreeTypeLabelRenderStrategy.h>
#include <vtkLabelHierarchy.h>
#include <vtkSelectVisiblePoints.h>

// For VTK 7
//@{
#include <vtkCellType.h>
//@}

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor2D.h"
#include "FITKGraphCommons.h"

// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

// Global data
#include "FITK_Kernel/FITKCore/FITKDataRepo.h"

// Data manager
#include "FITKSetSurfPolyProvider.h"

// Data
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaSpringDashpotPoint2Point.h"

namespace Exchange
{
    FITKGraphObjectSpringPoint2Point::FITKGraphObjectSpringPoint2Point(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaSpringDashpotPoint2Point* springData)
        : FITKGraphObjectMarkBase(caseData, springData)
    {
        if (!springData)
        {
            return;
        }

        // Initialize.
        init();

        // Generate the spring's node poly data for glyph.
        bool flag = generatePolyData(springData);
        if (!flag)
        {
            return;
        }

        // Set the layer need to be rendered.
        setRenderLayer(0, 1);

        // Generate actor.
        generateGraph();

        // Update the visibility.
        updateVisibility();
        
        // Save if the bounds are fixed or dynamic.
        m_hasFixedBounds = false;
    }

    FITKGraphObjectSpringPoint2Point::~FITKGraphObjectSpringPoint2Point()
    {
        // Delete the source.
        deleteVtkObj(m_sphereSource);

        // Delete poly data.
        //@{
        deleteVtkObj(m_points);
        deleteVtkObj(m_ugrid);
        deleteVtkObj(m_modelIdArray);
        //@}

        // Delete poly data for text.
        //@{
        deleteVtkObj(m_textProperty);     
        deleteVtkObj(m_pointsText);
        deleteVtkObj(m_textArray);
        deleteVtkObj(m_ugridText);
        deleteVtkObj(m_labelHieText);
        //@}

        // Delete extractor pointers.
        //@{
        deleteVtkObj(m_selectNode);
        deleteVtkObj(m_section);
        deleteVtkObj(m_extractSelection);
        deleteVtkObj(m_idArray);
        //@}

        // Delete extractor pointers for text.
        //@{
        deleteVtkObj(m_selectNodeText);
        deleteVtkObj(m_sectionText);
        deleteVtkObj(m_extractSelectionText);
        //@}

        // Delete the filter.
        //@{
        deleteVtkObj(m_glyph);
        deleteVtkObj(m_distanceToCamera);
        //@}

        // Delete pointer for advance highlight mode.
        //@{
        deleteVtkObj(m_pointsAdv);
        deleteVtkObj(m_ugridAdv); 
        deleteVtkObj(m_pointsTextAdv);
        deleteVtkObj(m_textArrayAdv);
        deleteVtkObj(m_ugridTextAdv);
        deleteVtkObj(m_glyphAdv);
        deleteVtkObj(m_distanceToCameraAdv);
        //@}

        // Delete visible points filter.
        deleteVtkObj(m_visiblePointsText);
    }

    void FITKGraphObjectSpringPoint2Point::setVisible(bool visibility)
    {
        // Show or hide glyph, line and label actor.
        if (m_fActor)
        {
            m_fActor->SetVisibility(visibility);
        }

        if (m_fActorLine)
        {
            m_fActorLine->SetVisibility(visibility);
        }

        if (m_fActorText2D)
        {
            m_fActorText2D->SetVisibility(visibility);
        }
    }

    void FITKGraphObjectSpringPoint2Point::init()
    {
        // Create the source.
        m_sphereSource = vtkSphereSource::New();
        m_sphereSource->SetRadius(1.);
        m_sphereSource->SetPhiResolution(6);
        m_sphereSource->SetThetaResolution(6);

        // Create poly data for text.
        //@{
        m_ugridText = vtkUnstructuredGrid::New();
        m_pointsText = vtkPoints::New();
        m_ugridText->SetPoints(m_pointsText);
        m_textArray = vtkStringArray::New();
        m_textArray->SetName("_label_");
        m_textArray->SetNumberOfComponents(1);
        m_ugridText->GetPointData()->AddArray(m_textArray);
        //@}

        // Create poly data for glyph.
        //@{
        m_ugrid = vtkUnstructuredGrid::New();
        m_points = vtkPoints::New();
        m_ugrid->SetPoints(m_points);
        m_modelIdArray = vtkIntArray::New();
        m_modelIdArray->SetNumberOfComponents(1);
        //@}

        // Create text property.
        //@{
        m_textProperty = vtkTextProperty::New();
        m_textProperty->SetBold(false);
        m_textProperty->SetFontSize(m_fontSize);

        // Get the spring color.
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_springColor, color3);
        m_textProperty->SetColor(color3);
        m_textProperty->SetFontFamilyToArial();
        //@}

        // Initialize the extractor.
        //@{
        m_selectNode = vtkSelectionNode::New();
        m_section = vtkSelection::New();
        m_extractSelection = vtkExtractSelection::New();
        m_selectNode->SetContentType(vtkSelectionNode::INDICES);
        m_selectNode->SetFieldType(vtkSelectionNode::SelectionField::CELL);
        m_section->AddNode(m_selectNode);
        m_extractSelection->SetInputData(1, m_section);

        m_idArray = vtkIdTypeArray::New();
        m_selectNode->SetSelectionList(m_idArray);
        m_extractSelection->SetInputData(m_ugrid);
        //@}

        // Initialize the extractor for text.
        //@{
        m_selectNodeText = vtkSelectionNode::New();
        m_sectionText = vtkSelection::New();
        m_extractSelectionText = vtkExtractSelection::New();
        m_selectNodeText->SetContentType(vtkSelectionNode::INDICES);
        m_selectNodeText->SetFieldType(vtkSelectionNode::SelectionField::POINT);
        m_sectionText->AddNode(m_selectNodeText);
        m_extractSelectionText->SetInputData(1, m_sectionText);

        // Use the id array for glyph.
        m_selectNodeText->SetSelectionList(m_idArray);
        m_extractSelectionText->SetInputData(m_ugridText);
        //@}

        // Initialize pointer for advance highlight mode.
        //@{
        m_pointsAdv = vtkPoints::New();
        m_ugridAdv = vtkUnstructuredGrid::New();
        m_ugridAdv->SetPoints(m_pointsAdv);

        m_pointsTextAdv = vtkPoints::New();
        m_ugridTextAdv = vtkUnstructuredGrid::New();
        m_ugridTextAdv->SetPoints(m_pointsTextAdv);
        m_textArrayAdv = vtkStringArray::New();
        m_textArrayAdv->SetName("_label_");
        m_textArrayAdv->SetNumberOfComponents(1);
        m_ugridTextAdv->GetPointData()->AddArray(m_textArrayAdv);
        //@}

        // Create visible points filter.
        m_visiblePointsText = vtkSelectVisiblePoints::New();

        // Create label hierarchy.
        //@{
        m_labelHieText = vtkPointSetToLabelHierarchy::New();
        m_labelHieText->SetInputConnection(m_visiblePointsText->GetOutputPort());
        m_labelHieText->SetTargetLabelCount(1);
        m_labelHieText->SetMaximumDepth(15);
        m_labelHieText->SetLabelArrayName(m_textArray->GetName());
        m_labelHieText->SetTextProperty(m_textProperty);
        //@}

        // Initialize the glyph size.
        m_glyphSize = 20.;
    }

    void FITKGraphObjectSpringPoint2Point::initActors()
    {
        // Get the color of spring.
        double color3[3]{ 0., 0., 0. };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_springColor, color3);

        // Create the actor for glyph.
        m_fActor = FITKGraphActor::New();
        m_fActor->setGraphObject(this);
        m_fActor->SetPickable(false);
        m_fActor->SetVisibility(true);
        m_fActor->setScalarVisibility(false);
        m_fActor->GetProperty()->SetRepresentation(1);
        m_fActor->GetProperty()->SetColor(color3);
        m_fActor->GetProperty()->SetLineWidth(1);
        m_fActor->GetProperty()->SetNormalScale(0);
        setLightPropertiesForLine(m_fActor);
        addActor(m_fActor);

        // Create the actor for lines.
        m_fActorLine = FITKGraphActor::New();
        m_fActorLine->setGraphObject(this);
        m_fActorLine->SetPickable(false);
        m_fActorLine->SetVisibility(true);
        m_fActorLine->setScalarVisibility(false);
        m_fActorLine->GetProperty()->SetRepresentation(1);
        m_fActorLine->GetProperty()->SetColor(color3);
        m_fActorLine->GetProperty()->SetLineWidth(2);
        // setLightPropertiesForLine(m_fActorLine);
        addActor(m_fActorLine);

        // Initialize fragment shader code.
        //@{
        // Not support for now.
        //@}

        // Create the actor for labels.
        m_fActorText2D = FITKGraphActor2D::New();
        m_fActorText2D->setGraphObject(this);
        m_fActorText2D->SetPickable(false);
        m_fActorText2D->SetVisibility(true);
        m_fActorText2D->setActorType(ActorType2D::TextActor);
        m_fActorText2D->GetProperty()->SetColor(color3);
        addActor(m_fActorText2D);

        // Get the color of spring advance highlight.
        double colorAdv3[3]{ 0., 0., 0. };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_springHighlightColor, colorAdv3);

        // Create the actor for glyph.
        m_fActorAdv = FITKGraphActor::New();
        m_fActorAdv->setGraphObject(this);
        m_fActorAdv->SetPickable(false);
        m_fActorAdv->SetVisibility(false);
        m_fActorAdv->setScalarVisibility(false);
        m_fActorAdv->GetProperty()->SetRepresentation(1);
        m_fActorAdv->GetProperty()->SetColor(colorAdv3);
        m_fActorAdv->GetProperty()->SetLineWidth(1);
        setLightPropertiesForLine(m_fActorAdv);
        addActor(m_fActorAdv);

        // Create the actor for lines.
        m_fActorLineAdv = FITKGraphActor::New();
        m_fActorLineAdv->setGraphObject(this);
        m_fActorLineAdv->SetPickable(false);
        m_fActorLineAdv->SetVisibility(false);
        m_fActorLineAdv->setScalarVisibility(false);
        m_fActorLineAdv->GetProperty()->SetRepresentation(1);
        m_fActorLineAdv->GetProperty()->SetColor(colorAdv3);
        m_fActorLineAdv->GetProperty()->SetLineWidth(4);
        // setLightPropertiesForLine(m_fActorLineAdv);
        addActor(m_fActorLineAdv);

        // Create the actor for labels.
        m_fActorText2DAdv = FITKGraphActor2D::New();
        m_fActorText2DAdv->setGraphObject(this);
        m_fActorText2DAdv->SetPickable(false);
        m_fActorText2DAdv->SetVisibility(false);
        m_fActorText2DAdv->setActorType(ActorType2D::TextActor);
        m_fActorText2DAdv->GetProperty()->SetColor(colorAdv3);
        addActor(m_fActorText2DAdv);
    }

    void FITKGraphObjectSpringPoint2Point::clearModelsInfo()
    {
        // Clear model ids.
        m_modelIds.clear();
        m_modelVisibilityHash.clear();
        m_lastModelIdNodeIdHash.clear();

        resetVtkObj(m_modelIdArray);
    }

    void FITKGraphObjectSpringPoint2Point::clearData()
    {
        // Clear all data.
        resetVtkObj(m_points);
        resetVtkObj(m_ugrid);
        resetVtkObj(m_pointsText);
        resetVtkObj(m_textArray);

        // Reset advance data.
        clearAdvanceData();

        // Clear the model infomation.
        clearModelsInfo();
    }

    void FITKGraphObjectSpringPoint2Point::highlight(QColor color)
    {
        // If the object is invisible, then return.
        if (!getDataVisibility())
        {
            return;
        }

        // Get the spring highlight color.
        double color3[3] = { 0 ,0, 0 };
        if (color.isValid())
        {
            FITKGraphCommons::QColorToDouble3(color, color3);
        }
        else
        {
            FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_springHighlightColor, color3);
        }      
        
        // Set the highlight color for glyph.
        if (m_fActor)
        {
            m_fActor->GetProperty()->SetColor(color3);
        }

        // Set the highlight color for lines.
        if (m_fActorLine)
        {
            m_fActorLine->GetProperty()->SetColor(color3);
        }

        // Set the highlight color for labels.
        if (m_textProperty)
        {
            m_textProperty->SetColor(color3);
        }

        FITKGraphObject3D::highlight();
    }

    void FITKGraphObjectSpringPoint2Point::disHighlight()
    {
        // Get the spring default color.
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_springColor, color3);

        // Set the default color for glyph.
        if (m_fActor)
        {
            m_fActor->GetProperty()->SetColor(color3);
        }

        // Set the default color for lines.
        if (m_fActorLine)
        {
            m_fActorLine->GetProperty()->SetColor(color3);
        }

        // Set the default color for labels.
        if (m_textProperty)
        {
            m_textProperty->SetColor(color3);
        }

        FITKGraphObject3D::disHighlight();
    }

    void FITKGraphObjectSpringPoint2Point::advanceHighlight(QVariant details, QColor color)
    {
        // Reset data first.
        clearAdvanceData();

        Interface::FITKAbaSpringDashpotPoint2Point* springData = dynamic_cast<Interface::FITKAbaSpringDashpotPoint2Point*>(_dataObj);
        if (!springData)
        {
            return;
        }

        if (!m_pointsAdv || !m_pointsTextAdv || !m_textArrayAdv || !m_ugridAdv || !m_ugridTextAdv)
        {
            return;
        }

        // Check the given indice of spring pair.
        if (!details.isValid())
        {
            return;
        }

        QVariantList vList = details.toList();
        if (vList.count() == 0)
        {
            return;
        }

        // K & C & K + C
        QString labelText;

        if (!m_useStiff && m_useCoeff)
        {
            labelText = "K";
        }
        else if (m_useStiff && !m_useCoeff)
        {
            labelText = "C";
        }
        else if (m_useStiff && m_useCoeff)
        {
            labelText = "K + C";
        }

        for (const QVariant & var : vList)
        {
            // Get the line by the given data id.
            int lineId = var.toInt();
            Interface::FITKAbaAssEle3D2* line = springData->getDataByID(lineId);
            if (!line)
            {
                continue;
            }

            // Generate data.
            //@{
            // Get the positions.
            double coord_1[3]{ 0., 0., 0. };
            double coord_2[3]{ 0., 0., 0. };
            line->getCoor1(coord_1);
            line->getCoor2(coord_2);

            // Get the mid-point for text label.
            double midPoint[3]{ (coord_2[0] + coord_1[0]) / 2.,
                (coord_2[1] + coord_1[1]) / 2.,
                (coord_2[2] + coord_1[2]) / 2. };

            // Generate poly data for glyph and line.
            int ptLine_1 = m_pointsAdv->InsertNextPoint(coord_1);
            int ptLine_2 = m_pointsAdv->InsertNextPoint(coord_2);

            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(ptLine_1);
            cell->InsertNextId(ptLine_2);
            m_ugridAdv->InsertNextCell(VTKCellType::VTK_LINE, cell);

            // Generate poly data for label.
            m_pointsTextAdv->InsertNextPoint(midPoint);
            m_textArrayAdv->InsertNextValue(QStringToCharA(labelText));
            //@}
        }
    
        // Get highlight color.
        double color3[3] = { 0 ,0, 0 };
        if (color.isValid())
        {
            FITKGraphCommons::QColorToDouble3(color, color3);
        }
        else
        {
            FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_springHighlightColor, color3);
        }

        // Show the advance actors.
        //@{
        if (m_fActorAdv)
        {
            m_fActorAdv->GetProperty()->SetColor(color3);
            m_fActorAdv->SetVisibility(true);
        }

        if (m_fActorLineAdv)
        {
            m_fActorLineAdv->GetProperty()->SetColor(color3);
            m_fActorLineAdv->SetVisibility(true);
        }

        if (m_fActorText2DAdv)
        {
            m_textProperty->SetColor(color3);
            m_fActorText2DAdv->SetVisibility(true);
        }
        //@}
    }

    void FITKGraphObjectSpringPoint2Point::disAdvanceHighlight()
    {
        // Reset data.
        clearAdvanceData();

        // Hide the advance actors.
        //@{
        if (m_fActorAdv)
        {
            m_fActorAdv->SetVisibility(false);
        }

        if (m_fActorLineAdv)
        {
            m_fActorLineAdv->SetVisibility(false);
        }

        if (m_fActorText2DAdv)
        {
            m_fActorText2DAdv->SetVisibility(false);
        }
        //@}
    }

    void FITKGraphObjectSpringPoint2Point::update(bool forceUpdate)
    {
        Interface::FITKAbaSpringDashpotPoint2Point* springData = dynamic_cast<Interface::FITKAbaSpringDashpotPoint2Point*>(_dataObj);
        if (!springData)
        {
            return;
        }

        // K & C & K + C
        QString labelText;

        bool needUpdateGrid = false;
        bool needUpdateText = false;

        if (forceUpdate)
        {
            needUpdateGrid = true;
            needUpdateText = true;
        }
        else
        {
            // Check boolean.
            needUpdateText = springData->getStiffnessState() != m_useStiff ||
                springData->getDaspotState() != m_useCoeff;

            if (!springData->getStiffnessState() && springData->getDaspotState())
            {
                labelText = "K";
            }
            else if (springData->getStiffnessState() && !springData->getDaspotState())
            {
                labelText = "C";
            }
            else if (springData->getStiffnessState() && springData->getDaspotState())
            {
                labelText = "K + C";
            }

            // Get the spring pair count.
            int nPair = springData->getDataCount();

            if (m_lastModelIdNodeIdHash.count() == nPair)
            {
                for (int i = 0; i < nPair; i++)
                {
                    // Get the spring line.
                    Interface::FITKAbaAssEle3D2* line = springData->getDataByIndex(i);
                    if (!line)
                    {
                        continue;
                    }

                    // Get the model( instance ) id of the node.
                    int modelId_1 = -1, modelId_2 = -1;

                    // Get the node id of the model.
                    int nodeId_1 = -1, nodeId_2 = -1;

                    line->getNode1(modelId_1, nodeId_1);
                    line->getNode2(modelId_2, nodeId_2);

                    QPair<int, int> pair_1 = m_lastModelIdNodeIdHash[i * 2];
                    QPair<int, int> pair_2 = m_lastModelIdNodeIdHash[i * 2 + 1];

                    // Check the value changed.
                    if (pair_1.first != modelId_1 || pair_1.second != nodeId_1 || 
                        pair_2.first != modelId_2 || pair_2.second != nodeId_2)
                    {
                        needUpdateGrid = true;
                        break;
                    }
                }
            }
            else
            {
                needUpdateGrid = true;
            }
        }    

        // Generate the poly data.
        if (needUpdateGrid)
        {
            // Clear data first.
            clearData();

            // Re-generate grid data.
            generatePolyData(springData);
        }
        else
        {
            // Re-generate the text only.
            if (needUpdateText)
            {
                int nText = m_textArray->GetNumberOfValues();
                resetVtkObj(m_textArray);

                for (int i = 0; i < nText; i++)
                {
                    m_textArray->InsertNextValue(QStringToCharA(labelText));
                }
            }
        }

        // Update the whole visibility at last.
        updateVisibility();
    }

    void FITKGraphObjectSpringPoint2Point::updateVisibility()
    {
        // Update the full actor.
        FITKGraphObject3D::updateVisibility();

        if (!m_idArray || !m_modelIdArray || !m_ugrid || !m_ugridText)
        {
            return;
        }

        QHash<int, bool> modelVisibilityHash;

        // Save the visibility first, and check if the visbility has changed at the same time.
        bool hasChanged = false;
        for (int modelId : m_modelIds)
        {
            bool parentVisible = getParentDataVisibility(modelId);
            bool oldVisibility = false;
            if (m_modelVisibilityHash.contains(modelId))
            {
                oldVisibility = m_modelVisibilityHash[modelId];
            }

            if (parentVisible != oldVisibility)
            {
                hasChanged = true;
            }

            if (!modelVisibilityHash.contains(modelId))
            {
                modelVisibilityHash.insert(modelId, parentVisible);
            }
        }

        // If the visibility hasn't changed, then return.
        if (!hasChanged && !m_modelVisibilityHash.isEmpty())
        {
            return;
        }

        // Reset the extractor's ids.
        resetVtkObj(m_idArray);

        // Update the extractor's ids.
        for (int i = 0; i < m_ugrid->GetNumberOfCells(); i++)
        {
            bool visibility = getCellVisibilityWithModel(m_ugrid->GetCell(i), m_modelIdArray, modelVisibilityHash);
            if (visibility)
            {
                m_idArray->InsertNextValue(i);
            }
        }

        // Update the data.
        m_ugrid->Modified();
        m_ugridText->Modified();

        // Save the flags.
        m_modelVisibilityHash = modelVisibilityHash;
    }

    void FITKGraphObjectSpringPoint2Point::generateActors()
    {
        // Create glyph filter.
        m_glyph = vtkGlyph3D::New();
        m_glyph->SetSourceConnection(m_sphereSource->GetOutputPort());

        // Set the input data and connection.
        m_extractSelection->SetInputData(m_ugrid);
        m_distanceToCamera = vtkDistanceToCamera::New();
        m_distanceToCamera->SetScreenSize(m_glyphSize);
        m_distanceToCamera->SetInputConnection(m_extractSelection->GetOutputPort());

        // Add the cell array name need to be used.      
#if VTK_MAJOR_VERSION < 8
        m_glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "DistanceToCamera");
#else
        m_glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, m_distanceToCamera->GetDistanceArrayName());
#endif

        m_glyph->SetScaling(true);

        m_glyph->SetInputConnection(m_distanceToCamera->GetOutputPort());
        m_fActor->setInputConnection(m_glyph->GetOutputPort());

        // Initialize the line actor.
        m_fActorLine->setInputConnection(m_extractSelection->GetOutputPort());

        // Initalize the text actor.
        //@{
        m_visiblePointsText->SetInputConnection(m_extractSelectionText->GetOutputPort());

        // Text mapper and strategy.
        //@{
        vtkSmartPointer<vtkLabelPlacementMapper> mapperText = vtkSmartPointer<vtkLabelPlacementMapper>::New();
        mapperText->UseDepthBufferOff();
        mapperText->SetShapeToNone();
        mapperText->SetOutputTraversedBounds(false);
        mapperText->UseUnicodeStringsOff();

        vtkSmartPointer<vtkFreeTypeLabelRenderStrategy> strategyText = vtkSmartPointer<vtkFreeTypeLabelRenderStrategy>::New();

        // Set input connecton of label hierarchy and strategy.
        mapperText->SetInputConnection(0, m_labelHieText->GetOutputPort());
        mapperText->SetRenderStrategy(strategyText);

        // Show all labels
        mapperText->SetPlaceAllLabels(true);
        mapperText->SetMaximumLabelFraction(1.);

        mapperText->SetIteratorType(vtkLabelHierarchy::FULL_SORT);
        mapperText->Update();

        m_fActorText2D->SetMapper(mapperText);
        //@}
        //@}
    }

    void FITKGraphObjectSpringPoint2Point::generateActorsAdvance()
    {
        // Create glyph filter.
        m_glyphAdv = vtkGlyph3D::New();
        m_glyphAdv->SetSourceConnection(m_sphereSource->GetOutputPort());

        // Set the input data and connection.
        m_distanceToCameraAdv = vtkDistanceToCamera::New();
        m_distanceToCameraAdv->SetScreenSize(m_glyphSize);
        m_distanceToCameraAdv->SetInputData(m_ugridAdv);

        // Add the cell array name need to be used.      
#if VTK_MAJOR_VERSION < 8
        m_glyphAdv->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "DistanceToCamera");
#else
        m_glyphAdv->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, m_distanceToCameraAdv->GetDistanceArrayName());
#endif

        m_glyphAdv->SetScaling(true);

        m_glyphAdv->SetInputConnection(m_distanceToCameraAdv->GetOutputPort());
        m_fActorAdv->setInputConnection(m_glyphAdv->GetOutputPort());

        // Initialize the line actor.
        m_fActorLineAdv->setInputDataObject(m_ugridAdv);

        // Initalize the text actor.
        //@{
        // Label hierarchy.
        //@{
        vtkSmartPointer<vtkPointSetToLabelHierarchy> textData = vtkSmartPointer<vtkPointSetToLabelHierarchy>::New();
        textData->SetInputData(m_ugridTextAdv);
        textData->SetTargetLabelCount(1);
        textData->SetMaximumDepth(15);
        textData->SetLabelArrayName(m_textArrayAdv->GetName());
        textData->SetTextProperty(m_textProperty);
        textData->Update();
        //@}

        // Text mapper and strategy.
        //@{
        vtkSmartPointer<vtkLabelPlacementMapper> mapperText = vtkSmartPointer<vtkLabelPlacementMapper>::New();
        mapperText->UseDepthBufferOff();
        mapperText->SetShapeToNone();
        mapperText->SetOutputTraversedBounds(false);
        mapperText->UseUnicodeStringsOff();

        vtkSmartPointer<vtkFreeTypeLabelRenderStrategy> strategyText = vtkSmartPointer<vtkFreeTypeLabelRenderStrategy>::New();

        // Set input connecton of label hierarchy and strategy.
        mapperText->SetInputConnection(0, textData->GetOutputPort());
        mapperText->SetRenderStrategy(strategyText);

        // Show all labels
        mapperText->SetPlaceAllLabels(true);
        mapperText->SetMaximumLabelFraction(1.);

        mapperText->SetIteratorType(vtkLabelHierarchy::FULL_SORT);
        mapperText->Update();

        m_fActorText2DAdv->SetMapper(mapperText);
        //@}
        //@}
    }

    void FITKGraphObjectSpringPoint2Point::generateGraph()
    {
        if (!m_ugrid || !m_extractSelection || !m_sphereSource)
        {
            return;
        }

        // Initialize the actors.
        initActors();

        // Generate base actors.
        generateActors();

        // Generate advance actors.
        generateActorsAdvance();
    }

    bool FITKGraphObjectSpringPoint2Point::generatePolyData(Interface::FITKAbaSpringDashpotPoint2Point* springData)
    {
        // K & C & K + C
        QString labelText;

        // Save states.
        m_useCoeff = springData->getDaspotState();
        m_useStiff = springData->getStiffnessState();

        if (!m_useStiff && m_useCoeff)
        {
            labelText = "K";
        }
        else if (m_useStiff && !m_useCoeff)
        {
            labelText = "C";
        }
        else if (m_useStiff && m_useCoeff)
        {
            labelText = "K + C";
        }

        // Get the spring pair count.
        int nPair = springData->getDataCount();

        for (int i = 0; i < nPair; i++)
        {
            // Get the spring line.
            Interface::FITKAbaAssEle3D2* line = springData->getDataByIndex(i);
            if (!line)
            {
                continue;
            }

            // Get the model( instance ) id of the node.
            int modelId_1 = -1, modelId_2 = -1;

            // Get the node id of the model.
            int nodeId_1 = -1, nodeId_2 = - 1;

            line->getNode1(modelId_1, nodeId_1);
            line->getNode2(modelId_2, nodeId_2);

            // Get the positions.
            double coord_1[3]{ 0., 0., 0. };
            double coord_2[3]{ 0., 0., 0. };
            line->getCoor1(coord_1);
            line->getCoor2(coord_2);

            // Get the mid-point for text label.
            double midPoint[3]{ (coord_2[0] + coord_1[0]) / 2.,
                (coord_2[1] + coord_1[1]) / 2.,
                (coord_2[2] + coord_1[2]) / 2. };

            // Generate poly data for glyph and line.
            int ptLine_1 = m_points->InsertNextPoint(coord_1);
            int ptLine_2 = m_points->InsertNextPoint(coord_2);
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(ptLine_1);
            cell->InsertNextId(ptLine_2);
            m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);

            // Generate poly data for label.
            m_pointsText->InsertNextPoint(midPoint);
            m_textArray->InsertNextValue(QStringToCharA(labelText));

            // Save model ids.
            if (!m_modelIds.contains(modelId_1))
            {
                m_modelIds.push_back(modelId_1);
            }

            if (!m_modelIds.contains(modelId_2))
            {
                m_modelIds.push_back(modelId_2);
            }

            // Save the model and node id.
            m_lastModelIdNodeIdHash.push_back(QPair<int, int>(modelId_1, nodeId_1));
            m_lastModelIdNodeIdHash.push_back(QPair<int, int>(modelId_2, nodeId_2));

            // Add model id to array.( For extractor. )
            m_modelIdArray->InsertNextValue(modelId_1);
            m_modelIdArray->InsertNextValue(modelId_2);
        }

        return true;
    }

    void FITKGraphObjectSpringPoint2Point::simplified(bool enabled)
    {
        if (m_graphSimplified)
        {
            bool dataVisible = getDataVisibility();

            // Change the actor visibility while changing the camera.
            if (dataVisible)
            {
                if (m_fActor)
                {
                    m_fActor->SetVisibility(!enabled);
                }

                if (m_fActorLine && dataVisible)
                {
                    m_fActorLine->SetVisibility(!enabled);
                }

                if (m_fActorText2D && dataVisible)
                {
                    m_fActorText2D->SetVisibility(!enabled);
                }
            }
        }
        else
        {
            // Disabled or enable the visible points while rotating the camera.
            //@{
            // Check if the spring is highlighting.
            enableVisiblePoints(!enabled && !isHighlighting());
            //@}
        }
    }

    bool FITKGraphObjectSpringPoint2Point::isRelatedTo(int dataObjId)
    {
        // Check if the id is the self-object id.
        bool isTheObj = FITKGraphObject3D::isRelatedTo(dataObjId);
        if (isTheObj)
        {
            return true;
        }

        // Check if the model ids contains the data id.
        bool hasModelRelated = m_modelIds.contains(dataObjId);
        if (hasModelRelated)
        {
            return true;
        }

        return false;
    }

    void FITKGraphObjectSpringPoint2Point::setFITKRender(Comp::FITKGraphRender* render)
    {
        if (!render)
        {
            return;
        }

        vtkRenderer* renderer = render->getRenderer();
        if (!renderer)
        {
            return;
        }

        // Set the renderer for calculating size.
        if (m_distanceToCamera)
        {
            m_distanceToCamera->SetRenderer(renderer);
        }

        if (m_distanceToCameraAdv)
        {
            m_distanceToCameraAdv->SetRenderer(renderer);
        }

        // Set the renderer for check if the points are visible.
        if (m_visiblePointsText)
        {
            m_visiblePointsText->SetRenderer(renderer);
        }

        // Save the render.
        this->Superclass::setFITKRender(render);
    }

    void FITKGraphObjectSpringPoint2Point::clearAdvanceData()
    {
        // Reset the grid.( And also text array. )
        //@{
        resetVtkObj(m_pointsAdv);
        resetVtkObj(m_ugridAdv);
        resetVtkObj(m_pointsTextAdv);
        resetVtkObj(m_ugridTextAdv);
        resetVtkObj(m_textArrayAdv);
        //@}
    }

    void FITKGraphObjectSpringPoint2Point::enableVisiblePoints(bool enabled)
    {
        // The label text.
        if (m_labelHieText && m_visiblePointsText)
        {
            m_labelHieText->RemoveAllInputConnections(0);
            m_labelHieText->RemoveAllInputs();

            if (enabled)
            {
                m_labelHieText->SetInputConnection(m_visiblePointsText->GetOutputPort());
            }
            else
            {
                m_labelHieText->SetInputData(m_visiblePointsText->GetOutput());
            }
        }
    }
}   // namespace Exchange